﻿// -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	SmartLib
//  File name:	SlidingApproximator.cs
//  Developer:	Igor Solomatov
//  Created:	04.11.2012
// 
// -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;

namespace Smart
{
	public class SlidingApproximator<TResultValue, TInternalValue, TSourceValue>
	{
		private readonly int _size;
		private readonly TInternalValue _initial;
		private readonly Func<TInternalValue, TSourceValue, TInternalValue> _adder;
		private readonly Func<TInternalValue, TSourceValue, TInternalValue> _remover;
		private readonly Func<TInternalValue, int, TResultValue> _divider;

		private TInternalValue _accumulator;
		private Queue<TSourceValue> _bucket;

		public TResultValue Current;

		public SlidingApproximator(int size, TInternalValue initial, Func<TInternalValue, TSourceValue, TInternalValue> adder, Func<TInternalValue, TSourceValue, TInternalValue> remover, Func<TInternalValue, int, TResultValue> divider)
		{
			_size = size;
			_initial = initial;
			_adder = adder;
			_remover = remover;
			_divider = divider;
		}

		public void Init()
		{
			_accumulator = default(TInternalValue);
			_bucket = new Queue<TSourceValue>(_size);
		}

		public void Reset()
		{
			_accumulator = default(TInternalValue);

			if (_bucket != null)
				_bucket.Clear();

			_bucket = null;
		}

		public bool Init(IEnumerator<TSourceValue> enumerator)
		{
			Init();

			var counter = 0;
			var accumulator = _initial;

			while (counter < _size && enumerator.MoveNext())
			{
				var current = enumerator.Current;

				_bucket.Enqueue(current);
				accumulator = _adder(accumulator, current);
				counter++;
			}

			if (counter != _size)
				return false;

			Current = _divider(_accumulator = accumulator, _size);

			return true;
		}

		public bool Init(IList<TSourceValue> items, int startIdx)
		{
			Init();

			if (items.Count < _size + startIdx)
				return false;

			var accumulator = _initial;

			for (int i = startIdx, iMax = _size + startIdx; i < iMax; i++)
			{
				var current = items[i];

				_bucket.Enqueue(current);
				accumulator = _adder(accumulator, current);
			}

			Current = _divider(_accumulator = accumulator, _size);

			return true;
		}

		public bool Next(IEnumerator<TSourceValue> _enumerator)
		{
			if (!_enumerator.MoveNext())
				return false;

			var current = _enumerator.Current;

			_bucket.Enqueue(current);
			Current = _divider(_accumulator = _adder(_remover(_accumulator, _bucket.Dequeue()), current), _size);

			return true;
		}

		public bool Next(TSourceValue current)
		{
			_bucket.Enqueue(current);
			Current = _divider(_accumulator = _adder(_remover(_accumulator, _bucket.Dequeue()), current), _size);

			return true;
		}
	}
}